using System;

namespace StreamingClient.TSSplitter
{
    internal class TSPacket
    {
        public TSPacket(byte[] buffer)
        {
            _packet = buffer;
            PID = (((_packet[1] << 8) | _packet[2]) & 0x1FFF);
            // bit 5 of byte 3
            HasAdaptionField = (((_packet[3] & 0x20) >> 5) > 0);
            if (HasAdaptionField)
                ADAPTION_FIELD_LENGTH= _packet[4];
            // bit 4 of byte 3
            HasPayload= (((_packet[3] & 0x10) >> 4) > 0);
            // bit 0-3 of Byte 3
            Counter=(_packet[3] & 0xF);
            if (HasAdaptionField)
                ESSENCE_OFFSET= 5 + ADAPTION_FIELD_LENGTH;
            else
                ESSENCE_OFFSET= 4;

            HasPayloadStart=((_packet[1] & 0x40) > 0);
            if (HasAdaptionField)
                HasPCR= ((_packet[5] & 0x10) == 0x10);
            if (HasPCR)
            {

                long m_base = (_packet[AF_OFFSET + 2] << 25) | (_packet[AF_OFFSET + 3] << 17) |
                              (_packet[AF_OFFSET + 4] << 9) | (_packet[AF_OFFSET + 5] << 1) |
                              ((_packet[AF_OFFSET + 6] & 0x80) >> 7);

                long m_ext = ((_packet[AF_OFFSET + 6] & 0x1) << 8) | _packet[AF_OFFSET + 7];

                PCR= TimeSpan.FromMilliseconds(((m_base * 300) + m_ext) / 27000);
            }
        }

        public byte[] _packet;

        public const int PAT_OFFSET = 6;           // begin of PAT Header
        public const int PMT_OFFSET = 6;           // begin of PMT Header
        public const int AF_OFFSET = 4;            // begin of Adaption Field


        public PESPacket PES
        {
            get
            {
                PESPacket info = new PESPacket(_packet, AF_OFFSET + ADAPTION_FIELD_LENGTH);
                if (HasPayloadStart && (info.START_CODE_PREFIX == 1) &&
                    ((info.STREAM_ID >= 0xC0) && info.STREAM_ID <= 0xEF))
                {
                    //$C0 bis $DF (110x xxxx) = AUDIO 
                    //$E0 bis $EF (1110 xxxx) = VIDEO 
#if DEBUG
                    // Debug.WriteLine("Found PES Header - PID: "+PID.ToString()+" PTS: " + info.PTS.ToString());
#endif
                    return info;
                }
                else
                    return null;

            }
        }



        public int PID{get; private set;}



        public bool HasAdaptionField
        {
            get; set;
        }



        public bool HasPayload
        {
            get; set;
        }



        public int Counter
        {
            get; set;
        }




        public byte ADAPTION_FIELD_LENGTH
        {
            get; set;
        }




        public int ESSENCE_OFFSET
        {
            get; set;
        }



        public bool HasPayloadStart
        {
            get; set;
        }


        public bool HasPCR
        {
            get; set;
        }


        public TimeSpan PCR
        {
            get; set;
        }

        public bool IsVideo
        {
            get
            {
                //Startcode: 00 00 01 E0
                return HasPayloadStart && (_packet[ESSENCE_OFFSET] == 0x00
                                           && _packet[ESSENCE_OFFSET + 1] == 0x00
                                           && _packet[ESSENCE_OFFSET + 2] == 0x01
                                           && _packet[ESSENCE_OFFSET + 3] == 0xE0);
            }
        }

        public byte[] GetCodecPrivateData()
        {
            //MPEG2 -
            //TS - MPEG2 Transport Stream
            //PES - MPEG2 Program Elementary Stream
            //PTS - Presentation Time Stamp
            //DTS - Decoding Time Stamp
            //AVC - Advanced Video Coding = H.264
            //SPS - AVC Sequence Parameter Set
            //PPS - AVC Picture Parameter Set
            //CPD - AVC Codec Private Data

            //47 40 45 30 07 10 59 0E E9 D7 7E 00 00 00 01 E0 4F 09 80 C0 0A 35 C8 79 F7 63 15 C8 79 BF 23 00 00 00 01 09 E0 00 00 00 01 67 ........ 00 00 00 01 68 EB EC B2 2C ...
            //TS-Header  |Adaption Field      |PES-Videostart|???        |  |PTS           |DTS           |AVC-Videostart   |SPS-Start     |SPS n=29|PPS-Start     |PPS        |
            //                                                            ^                                                 |CPD                                          |
            //                                                      Number of Bytes       


            var ptsDtsOffset = _packet[ESSENCE_OFFSET + 8];
            var spsOffset = ESSENCE_OFFSET + 8 + 1 + ptsDtsOffset + 6;
            byte[] cpd = new byte[5+29+5]; //startcode+sps+startcode+pps
            if (_packet[spsOffset + 4] != 0x67)
            {
                return null;
            }
            Buffer.BlockCopy(_packet,spsOffset,cpd,0,cpd.Length);
            return cpd;
        }
    }
}